/* 
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
   USA.
*/

#include <types.h>
#include <kerrno.h>
#include <io.h>

#include "tty.h"

/*
 * Keyboard ports and commands.
 * 
 * @see Ralf Brown's interrupt (and port) list	 
 * http://www-2.cs.cmu.edu/~ralf/files.html	 
 */

#define KBD_DATA_PORT               0x60

#define KBD_BREAKCODE_LIMIT         0x80
#define KBD_EXTENDED_SCANCODE       0xE0

#define KBD_IS_MAKECODE(c)          ((c) < KBD_BREAKCODE_LIMIT)
#define KBD_IS_BREAKCODE(c)         ((c) >= KBD_BREAKCODE_LIMIT)
#define KBD_BREAKCODE_2_MAKECODE(c) ((c) ^ KBD_BREAKCODE_LIMIT)

#define KBD_LEFTSHIFT_SCANCODE      0x2a
#define KBD_RIGHTSHIFT_SCANCODE     0x36

typedef __u8 scancode_t;

extern const char *kbd_regular_translate_table [];
extern const char *kbd_shift_translate_table [];

static const char **kbd_current_translate_table = kbd_regular_translate_table;
static int is_ext = false;

 static struct tty_device *tty;

void kbd_irq_handler ()
{

  scancode_t scancode;
  const char *key = NULL;

  scancode = inb (KBD_DATA_PORT);

  /* Mark that next interrupt wil give an extended scancode */
  if (scancode == KBD_EXTENDED_SCANCODE)
    {
      is_ext = true;
    }

  /* Handle extended scancode */
  else if (is_ext)
    {
      is_ext = false;
    }

  /* Normal scancode */
  else
    {
      /* Keypressed */
      if (KBD_IS_MAKECODE(scancode))
	{
	  /* If shift, change translation table */
	  if ((scancode == KBD_LEFTSHIFT_SCANCODE) ||
	      (scancode == KBD_RIGHTSHIFT_SCANCODE))
	    kbd_current_translate_table = kbd_shift_translate_table;

	  /* If normal key, compute the result using the translation
	     tables and the booleans */
	  else if (kbd_current_translate_table[scancode])
	    {
	      key = kbd_current_translate_table[scancode];
	    }
	}

      /* Key released */
      else
	{
	  scancode_t makecode = KBD_BREAKCODE_2_MAKECODE(scancode);

	  if ((makecode == KBD_LEFTSHIFT_SCANCODE) ||
	      (makecode == KBD_RIGHTSHIFT_SCANCODE))
	    kbd_current_translate_table = kbd_regular_translate_table;
	}
    }

  if (key)
    tty_add_chars (tty, key);
}


int  kbd_subsystem_setup(struct tty_device *t)
{
  tty = t;
  return OK;
}

